using AutoMapper;
using Enter.Dashboard.Api.Filters;
using Enter.Dashboard.Api.Tasks;
using Enter.Utils;
using Enter.Utils.CryptoExt;
using FluentScheduler;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Serialization;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using VueCliMiddleware;

namespace Enter.Dashboard.Api
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public static IFreeSql Fsql { get; private set; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
           
            Fsql = new FreeSql.FreeSqlBuilder()
                .UseConnectionString(FreeSql.DataType.MySql, GetConnectionString())
                .UseNoneCommandParameter(true)
                .Build();
        }

        private string GetConnectionString()
        {
            var conStr = Environment.GetEnvironmentVariable("ConnectionString");
            return string.IsNullOrWhiteSpace(conStr) ? Configuration.GetConnectionString("Default") : conStr;
        }
       

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
            services.AddDistributedMemoryCache();
            services.AddAutoMapper(config => {
                config.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.DoNotAllowNull()));//opt => opt.NullSubstitute("Ĭ��ֵ")
            }, Assembly.GetAssembly(typeof(Startup)));

            // NOTE: PRODUCTION Ensure this is the same path that is specified in your webpack output
            services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
            services.AddControllers(config =>{
                config.MaxModelValidationErrors = 5;
                config.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;              
                config.Filters.Add(typeof(ExceptionFilter));
                config.Filters.Add(typeof(AuthorizationFilter));
                //config.Filters.Add<ValidatorFilter>();
            })
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;// ��Сд;
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            })
            .AddFluentValidation(conf => {
                conf.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
                // �Զ�����;
                conf.RegisterValidatorsFromAssembly(Assembly.GetAssembly(typeof(Startup)));
            });

            services.Configure<ApiBehaviorOptions>(ops =>
            {
                //ops.SuppressModelStateInvalidFilter = true;// ʹ���Զ���ģ����֤ ʹ�������Ҫ�Զ���������IActionFilter
                ops.InvalidModelStateResponseFactory = (context) =>
                {
                    var errors = context.ModelState
                      .Values
                      .SelectMany(x => x.Errors.Select(p => p.ErrorMessage))
                      .ToList();
                    var result = new ResponseData<List<string>>(
                        errors
                        , string.Join(",", errors.Select(e => string.Format("{0}", e)).Take(5))
                        );
                    return new JsonResult(result);
                };
            });
			
			// https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-nswag?view=aspnetcore-5.0&tabs=visual-studio		
            // nswag openapi2tsclient /input:swagger.json /output:clients.ts	
            //services.AddSwaggerGen(c =>
            //{
            //    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Enter.Dashboard.Api", Version = "v1" });
            //});
            // Register the Swagger services
            services.AddSwaggerDocument();

            
            services.AddCors(options =>
            {
                options.AddPolicy("AllowAll",
                    builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
            });

            services.AddSingleton<IFreeSql>(Fsql);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<FluentSchedulerRegistry> logger)
        {
            app.UseSerilogRequestLogging(options =>
            {
                //// Customize the message template
                //options.MessageTemplate = "Handled {RequestPath}";

                //// Emit debug-level events instead of the defaults
                //options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;

                //// Attach additional properties to the request completion event
                //options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
                //{
                //    diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
                //    diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
                //};
            });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();

                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Enter.Dashboard.Api v1"));

                // Register the Swagger generator and the Swagger UI middlewares
                app.UseOpenApi();
                app.UseSwaggerUi3();
            }
            // NOTE: PRODUCTION uses webpack static files
            app.UseSpaStaticFiles();
            app.UseRouting();
            app.UseCors("AllowAll");
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();

                // NOTE: VueCliProxy is meant for developement and hot module reload
                // NOTE: SSR has not been tested
                // Production systems should only need the UseSpaStaticFiles() (above)
                // You could wrap this proxy in either
                // if (System.Diagnostics.Debugger.IsAttached)
                // or a preprocessor such as #if DEBUG
                endpoints.MapToVueCliProxy(
                    "{*path}",
                    new SpaOptions { SourcePath = "ClientApp" },
                    npmScript: null, //(System.Diagnostics.Debugger.IsAttached) ? "serve" : null,
                    regex: "Compiled successfully",
                    forceKill: true
                    );
            });

            // ��ʼ����;
            JobManager.Initialize(new FluentSchedulerRegistry(logger, Fsql, Configuration));
        }
    }
}
